package fem2.observer;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

import fem2.Debugger;
import fem2.Edge;
import fem2.Element;
import fem2.Face;
import fem2.Mesh;
import fem2.MeshUtilities;
import fem2.Model;
import fem2.Node;
import fem2.Observer;
import fem2.element.crack_sda.SDAElement;

/**
 * Crack observer for SDAElement
 * 
 * @author hbui
 * 
 */
public class GmshCrackObserver2 implements Observer {

	private Model m;

	private ArrayList<ArrayList<double[]>> crackPath;
	private Map<Element, Boolean> isCracked;
	private String gmshFileName = null;

	/**
	 * 
	 * @param m
	 * @param s
	 *            set to null if there is no need to export for post-processor
	 */
	public GmshCrackObserver2(Model m, String gmshFileName) {
		this.m = m;
		// if (!(m.getElement(0) instanceof SDAElement)) {
		// Debugger.error(this.getClass().getName() +
		// "only support for model contains SDAElement");
		// }
		this.gmshFileName = gmshFileName;
		crackPath = new ArrayList<ArrayList<double[]>>();
		isCracked = new HashMap<Element, Boolean>();
	}

	@Override
	public void fireStarted() {
	}

	@Override
	public void fireTimeStepStarted() {
	}

	@Override
	public void fireIterationStarted() {
	}

	@Override
	public void fireIterationFinished() {
	}

	@Override
	public void fireTimeStepFinished() {
		int ne = m.countElements();

		/*
		 * gmsh section
		 */
		if (gmshFileName != null) {
			for (int i = 0; i < ne; i++) {
				Element e = m.getElement(i);
				if (e instanceof SDAElement) {
					SDAElement ce = (SDAElement) e;
					if (ce.isCracked()) {
						if (isCracked.get(e) == null || isCracked.get(e) == false) {
							crackPath.add(ce.getX());
							isCracked.put(e, true);
						}
					}
				} else if (e instanceof fem2.element.crack_sda2.SDAElement) {
					fem2.element.crack_sda2.SDAElement ce = (fem2.element.crack_sda2.SDAElement) e;
					if (ce.isCracked()) {
						if (isCracked.get(e) == null || isCracked.get(e) == false) {
							crackPath.add(ce.getX());
							isCracked.put(e, true);
						}
					}
				} else {
					isCracked.put(e, false);
				}
			}
		}

	}

	@Override
	public void fireFinished() {
		/*
		 * if gmsh is set then export to gmsh mesh
		 */
		if (gmshFileName != null) {
			Mesh mesh = createMesh();
			try {
				MeshUtilities.writeGmshMesh(mesh, gmshFileName);
			} catch (IOException e) {
				Debugger.error("write Gmsh mesh file failed");
			}
		}
	}

	private Mesh createMesh() {
		Mesh mesh = m.getMesh();
		for (int i = 0; i < crackPath.size(); i++) {
			ArrayList<double[]> crackFace = crackPath.get(i);
			if (crackFace.size() == 2) {
				Node n1 = mesh.addNode(new Node(crackFace.get(0)));
				Node n2 = mesh.addNode(new Node(crackFace.get(1)));
				mesh.addEdge(new Edge(n1, n2));
			} else {
				int nn = crackFace.size();
				Node[] nodes = new Node[nn];
				for (int j = 0; j < nn; j++) {
					nodes[j] = mesh.addNode(new Node(crackFace.get(j)));
				}
				mesh.addFace(new Face(nodes));
			}
		}
		return mesh;
	}

}
